/*
 * @Description: 加载管理器
 * @Author: chenguanhui
 * @Date: 2019-08-13 19:48:19
 * @LastEditors  : xutao
 * @LastEditTime : 2020-01-13 18:51:49
 */

import ICallbackOwner from '../tool/ICallbackOwner';

export enum ResTypeEnum {
  Png = 'png',
  Jpg = 'jpg',
  Mp3 = 'mp3',
}

export class LoadMgr {
  private static loadSyncCache = {};

  /**
   * 加载prefab
   * @param url
   * @param completeCallback
   * @param owner
   */
  public static loadPrefab(
    url: string,
    completeCallback: (error: Error, resource: cc.Prefab) => void = null,
    owner: ICallbackOwner = null,
  ) {
    if (!url) {
      cc.error('loadPrefab url=null');
      return;
    }

    if (LoadMgr.loadSyncCache[url] != null) {
      cc.log('loadPrefabSync is loading');
      completeCallback(null, LoadMgr.loadSyncCache[url]);
      return;
    }

    LoadMgr.loadResource('loadPrefab', url, cc.Prefab, completeCallback, owner);
  }

  /**
   * 加载sprite
   * @param url
   * @param completeCallback
   * @param owner
   */
  public static loadSpriteFrame(
    url: string,
    completeCallback: (error: Error, ret: cc.SpriteFrame) => void = null,
    owner: ICallbackOwner = null,
  ): void {
    if (!url) {
      cc.error('loadSpriteFrame url=null');
      return;
    }

    if (url) {
      let escapedUrl = url.replace(/^resources\//, '').replace(/(.*)\.[^/]+$/, '$1');
      LoadMgr.loadResource('loadSpriteFrame', escapedUrl, cc.SpriteFrame, completeCallback, owner);
    }
  }

  /**
   * 加载JSON
   * @param url
   * @param completeCallback
   * @param owner
   */
  public static loadJSON(
    url: string,
    completeCallback: (error: Error, ret: any) => void = null,
    owner: ICallbackOwner = null,
  ): void {
    if (!url) {
      cc.error('loadJSON url=null');
      return;
    }

    if (url) {
      let escapedUrl = url.replace(/^resources\//, '').replace(/(.*)\.[^/]+$/, '$1');
      LoadMgr.loadResource('loadJSON', escapedUrl, cc.JsonAsset, completeCallback, owner);
    }
  }

  /**
   * 加载资源
   * @param funName
   * @param url
   * @param type
   * @param completeCallback
   * @param owner
   */
  private static loadResource(
    funName: string,
    url: string,
    type: typeof cc.Asset,
    completeCallback: (err: Error, ret: any) => void = null,
    owner: ICallbackOwner = null,
  ): boolean {
    if (!url) return false;

    cc.loader.loadRes(url, type, function (err, ret) {
      delete LoadMgr.loadSyncCache[url];
      LoadMgr.loadSyncCache[url] = null;

      if (err) {
        console.error(`failed to load ${url}: ${err.message}`);
      } else {
        LoadMgr.loadSyncCache[url] = ret;
        if (owner && !owner.isLive()) {
        } else {
          if (completeCallback) {
            // try {
            completeCallback(null, ret);
            //}
            //catch (e) {
            //LoadMgr.errorFunction(funName + '->loadResource()', e + ' res:' + url, owner);
            //}
          }
        }
      }
    });
    return true;
  }

  /**
   * 加载spine动效资源
   * @param url
   */
  public static loadSpRes(url: string, callback: (err: Error, ret: any) => void = null) {
    if (this.loadSyncCache[url]) {
      callback(null, this.loadSyncCache[url]);
      return;
    } else {
      cc.loader.loadRes(url, sp.SkeletonData, (err, data) => {
        this.loadSyncCache[url] = data;
        callback(err, data);
      });
    }
  }

  /**
   * 加载远程资源
   */
  private static async _loadRemote(url: string, type?: string): Promise<any> {
    return new Promise((rs, rj) => {
      cc.loader.load(type ? { url: url, type: type } : url, function (err: Error | null, res: any) {
        if (err) {
          rj(err);
        } else {
          rs(res);
        }
      });
    });
  }

  /**
   * 加载远程图片资源
   */
  public static loadRemoteImg(url: string, type: string, onLoad?: (sf: cc.SpriteFrame) => void): cc.SpriteFrame {
    let output = new cc.SpriteFrame();
    this._loadRemote(url, type).then((tex) => {
      output.setTexture(tex);
      onLoad && onLoad(output);
    });
    return output;
  }

  /**
   * 加载网络资源
   * @param path
   * @param callback
   * @param owner
   */
  public static load(path: string, callback: (err, ret) => void, owner: ICallbackOwner = null) {
    if (!path) {
      LoadMgr.errorFunction('LoadMgr->load', 'param error', owner);
      return;
    }

    let loadCallback = function (err, ret) {
      try {
        if (callback) callback(err, ret);
      } catch (e) {
        LoadMgr.errorFunction('LoadMgr->load->callback', e + ' res:' + path, owner);
      }
    };

    cc.loader.load(path, loadCallback);
  }

  /**
   * 加载目录
   * @param path
   * @param type
   * @param progressCallback
   * @param completeCallback
   */
  public static loadResourceDir(
    path: string,
    type: typeof cc.Asset,
    progressCallback: (completedCount: number, totalCount: number, item: any) => void,
    completeCallback: (error: Error, resource: any[], urls: string[]) => void,
  ) {
    if (!path) {
      LoadMgr.errorFunction('loadResourceDir', 'param error', null);
      return;
    }

    let progCallback = function (completedCount: number, totalCount: number, item: any) {
      try {
        if (progressCallback) progressCallback(completedCount, totalCount, item);
      } catch (e) {
        LoadMgr.errorFunction('loadResourceDir->progressCallback', e + ' res:' + path, null);
      }
    };

    let compCallback = function (error: Error, resource: any[], urls: string[]) {
      try {
        if (completeCallback) completeCallback(error, resource, urls);
      } catch (e) {
        LoadMgr.errorFunction('loadResourceDir->completeCallback', e + ' res:' + path, null);
      }
    };

    if (progressCallback) {
      cc.loader.loadResDir(path, type, progCallback, compCallback);
    } else {
      cc.loader.loadResDir(path, type, compCallback);
    }
  }

  /**
   * 加载多个资源
   * @param urlList
   * @param progressCallback
   * @param completeCallback
   */
  public static loadResourceArray(
    urlList: string[],
    progressCallback: (completedCount: number, totalCount: number, item: any) => void,
    completeCallback?: (error: Error, resource: any[]) => void,
  ): void {
    if (!urlList || urlList.length == 0) {
      LoadMgr.errorFunction('loadResourceArray', 'param error', null);
      return;
    }
    let resDebug = urlList[0];
    let progCallback = function (completedCount: number, totalCount: number, item: any) {
      try {
        if (progressCallback) progressCallback(completedCount, totalCount, item);
      } catch (e) {
        LoadMgr.errorFunction('loadResourceArray->progressCallback', e + ' res:' + resDebug, null);
      }
    };

    let compCallback = function (error: Error, resource: any[]) {
      try {
        if (completeCallback) completeCallback(error, resource);
      } catch (e) {
        LoadMgr.errorFunction('loadResourceArray->completeCallback', e + ' res:' + resDebug, null);
      }
    };
    if (progressCallback) {
      cc.loader.loadResArray(urlList, progCallback, compCallback);
    } else {
      cc.loader.loadResArray(urlList, compCallback);
    }
  }

  private static errorFunction(funcName: string, errInfo: string, owner: ICallbackOwner) {
    cc.error(errInfo);
  }

  /**
   * 加载多个资源（支持Promise）
   * @param urls {string[]} 资源路径组
   * @param urlType {typeof cc.Asset} 资源类型
   */
  public static loadResources(urls: string[], urlType: typeof cc.Asset): Promise<any> {
    let array: Promise<any>[] = [];
    for (let i = 0, len = urls.length; i < len; i++) {
      array.push(this.loadSingleResource(urls[i], urlType));
    }
    return Promise.all(array);
  }

  /**
   * 加载单个资源（支持Promise）
   * @param url {string} 资源路径
   * @param urlType {typeof cc.Asset} 资源类型
   */
  public static loadSingleResource(url: string, urlType: typeof cc.Asset): Promise<any> {
    return new Promise((resolve, reject) => {
      if (LoadMgr.loadSyncCache[url]) {
        resolve(LoadMgr.loadSyncCache[url]);
        console.log(`@machao->LoadMgr loadSingleResource 加载缓存资源 ${url}`);
        return;
      }
      cc.loader.loadRes(url, urlType, (err: Error, ret: cc.Asset) => {
        LoadMgr.loadSyncCache[url] = null;
        if (err) {
          console.error(`@machao->LoadMgr loadSingleResource 资源加载失败 ${url}: ${err.message}`);
          reject(err);
        } else {
          LoadMgr.loadSyncCache[url] = ret;
          resolve(ret);
        }
      });
    });
  }

  public static loadConfig(path: string, callback: Function) {
    LoadMgr.loadJSON('config/' + path, (err, jsonAsset) => {
      callback(err, jsonAsset.json);
    });
  }
}

// 导出方便使用
export let LOADER: LoadMgr = LoadMgr;
